package com.psbc.redislock.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.psbc.redislock.domain.Goods;
import com.psbc.redislock.lock.db.EasyDBLock;
import com.psbc.redislock.lock.redis.RedisLock;
import com.psbc.redislock.mapper.GoodsMapper;
import com.psbc.redislock.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Administrator
 * @description 针对表【t_goods】的数据库操作Service实现
 * @createDate 2023-07-18 21:40:40
 */
@Slf4j
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> {

    private static final String shoppingKey = "shopping";
    private static final ReentrantLock lock = new ReentrantLock();
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private EasyDBLock easyDBLock;


    /**
     * 购物
     *
     * @param name
     * @param num
     * @return
     * @throws Exception
     */
    public Result shopping(String name, Integer num) throws Exception {
        Goods goods = getOne(new QueryWrapper<Goods>().lambda().eq(Goods::getName, name), false);
        if (goods == null) {
            throw new Exception("没有此商品！");
        }
        Integer inventory = goods.getNum();
        if (inventory >= num) {
            goods.setNum(inventory - num);
            updateById(goods);
            return Result.OK("购买成功！");
        } else {
            return Result.NO("库存不足！");
        }
    }

    /**
     * 使用ReentrantLock
     *
     * @param name
     * @param num
     * @return
     * @throws Exception
     */
    public Result shoppingWithJdkLock(String name, Integer num) {
        try {
            lock.lock();
            return shopping(name, num);
        } catch (Exception e) {
            return Result.NO(e.getMessage());
        } finally {
            lock.unlock();
        }
    }

    /**
     * 购物使用数据库锁
     *
     * @param name
     * @param num
     * @return
     * @throws Exception
     */
    public Result shoppingWithDbLock(String name, Integer num) {
        String key = shoppingKey + name;
        boolean getLock = easyDBLock.acquireLock(key);
        if (!getLock) {
            return Result.NO("枪锁失败！");
        }
        try {
            return shopping(name, num);
        } catch (Exception e) {
            return Result.NO(e.getMessage());
        } finally {
            easyDBLock.releaseLock(key);
        }
    }


    /**
     * 使用redis锁
     *
     * @param name
     * @param num
     * @return
     * @throws Exception
     */
    public Result shoppingWithRedisLock(String name, Integer num) {
        String key = shoppingKey + name;
        //跟数据库锁一样，使用setIfAbsent：不存在就设值；
        Boolean getLock = redisTemplate.opsForValue().setIfAbsent(key, "lock");
        if (!getLock) {
            return Result.NO("枪锁失败！");
        }
        try {
            //考虑问题，1.此时如果宕机怎么办
            //2.此时如果执行时间比预期要长怎么办
            return shopping(name, num);
        } catch (Exception e) {
            return Result.NO(e.getMessage());
        } finally {
            redisTemplate.delete(key);
        }
    }

    /**
     * redis锁添加看门狗功能
     *
     * @param name
     * @param num
     * @return
     */
    public Result shoppingWithRedisLockAddWatchDod(String name, Integer num) {
        String key = shoppingKey + name;
        RedisLock redisLock = new RedisLock(redisTemplate, key);
        boolean getlock = redisLock.acquireLock();
        if (!getlock) {
            return Result.NO("枪锁失败！");
        }
        try {
            return shopping(name, num);
        } catch (Exception e) {
            return Result.NO(e.getMessage());
        } finally {
            redisLock.releaseLock();
        }
    }


    /**
     * 用注解实现
     *
     * @param name
     * @param num
     * @return
     * @throws Exception
     */
    @com.psbc.redislock.annotation.RedisLock(key = shoppingKey)
    public Result shoppingWithAnnotation(String name, Integer num) throws Exception {
        Goods goods = getOne(new QueryWrapper<Goods>().lambda().eq(Goods::getName, name), false);
        if (goods == null) {
            throw new Exception("没有此商品！");
        }
        Integer inventory = goods.getNum();
        if (inventory >= num) {
            goods.setNum(inventory - num);
            updateById(goods);
            return Result.OK("购买成功！");
        } else {
            return Result.NO("库存不足！");
        }
    }

}
